package titt.util.deploy;

import java.util.HashMap;
import java.util.Map;

import titt.head.HeadFactory;
import titt.head.Header;
import titt.head.request.BrowserRequestHead;
import titt.head.request.RequestHead;
import titt.service.ThreadSafe;
import titt.service.content.NuderResponse;
import titt.service.strategy.WebServer;
import titt.util.io.loader.BINFileLoader;
import titt.util.io.loader.FileStream;
import titt.util.io.loader.FileStreamLoader;

public class TITTWebServer implements WebServer, ThreadSafe {

	private final FileStreamLoader fls;
	private final Map<String, ResponseCache> 	cache = new HashMap<String, ResponseCache>(),
												gzipCache = new HashMap<String, ResponseCache>();

	private WebServer fallback;

	public TITTWebServer(FileStreamLoader fls, WebServer fallback) {
		this.fls = fls;
		this.fallback = fallback;
	}

	public TITTWebServer(String base, WebServer fallback) {
		this(new BINFileLoader(base), fallback);
	}

	public synchronized byte[] getFile(String path) throws Exception {
		return getCachedFile(path).getValue();
	}

	public synchronized Cache getCachedFile(String path) {
		return getCachedResponse(path, false).getCache();
	}

	public NuderResponse getResponseFile(String path) throws Exception {
		return getResponseFile(path, false);
	}
	
	public synchronized NuderResponse getResponseFile(String path, boolean compress) throws Exception {
		ResponseCache c = getCachedResponse(path, compress);
		
		if(c == null) return null;
		
		return c.getContent();
	}
		
	@Override
	public synchronized NuderResponse get(String path, RequestHead r) throws Exception {		
		NuderResponse c = getResponseFile(path, new BrowserRequestHead(r).supportsGZip());
				
		if(c == null)
			return fallback.get(path, r);		
		
		return c;
	}	

	@Override
	public Cache constructCache(String path) {
		if(path.endsWith("/titt"))
			return new TITTCache();
		
		if(path.endsWith("/runtime"))
			return new StrandsCache();
		
		FileStream fl;
		
		synchronized (fls) {			
			fl = fls.loadFileStream(path);
		}
		
		if(fl == null)
			return null;
		
		return path.endsWith(".js")? new CompilerCache(fl) : new FileCache(fl);
	}

	private synchronized ResponseCache getCachedResponse(String path, boolean compress) {
		Map<String, ResponseCache> cache = compress? gzipCache : this.cache;

		if(cache.containsKey(path))
			return cache.get(path);
		
		Cache c = fallback != null? fallback.constructCache(path) : null;
		
		if(c == null)
			c = constructCache(path);
		
		if(c == null)
			return null;		
		
		if(!cache.containsKey(path))
			cache.put(path, new ResponseCache(compress, new AutoUpdateCache(new JARCache(compress? new GZipCache(c) : c))));
		
		return cache.get(path);
	}
	
	private class ResponseCache {
		private final Cache c;
		private final boolean compressed;

		public ResponseCache(boolean compressed, Cache c) {
			this.c = c;
			this.compressed = compressed;
		}
		
		public Cache getCache() {
			return c;
		}

		public NuderResponse getContent() {
			if(compressed)
				return new NuderResponse(c.getValue(), 200, c.getMimeType(), HeadFactory.createHead(new Header("Content-Encoding", "gzip")));
			return new NuderResponse(c.getValue(), c.getMimeType());
		}

	}

}
